package com.chris.oauth2.service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.stereotype.Repository;

import com.chris.oauth2.C;
import com.chris.oauth2.Client;

@Repository
public class ClientService {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	public Client createClient(final Client client) {
		client.setCid(UUID.randomUUID().toString());
		client.setSecret(UUID.randomUUID().toString());
		final String sql = "insert into o_client(cid, `name`, secret, username, `password`, ct) values(?,?,?,?,md5(?),now())";

		GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
		jdbcTemplate.update(new PreparedStatementCreator() {

			@Override
			public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
				PreparedStatement psst = connection.prepareStatement(sql, new String[] { "id" });
				int count = 1;
				psst.setString(count++, client.getCid());
				psst.setString(count++, client.getName());
				psst.setString(count++, client.getSecret());
				psst.setString(count++, client.getUsername());
				psst.setString(count++, client.getPassword());
				return psst;
			}
		}, keyHolder);

		client.setId(keyHolder.getKey().longValue());
		return client;
	}

	public Client updateClient(Client client) {
		if (StringUtils.isNotBlank(client.getPassword())) {
			String sql = "update o_client set `password`=md5(?) where id=?";
			jdbcTemplate.update(sql, client.getPassword(), client.getId());
			return client;
		} else {
			String sql = "update o_client set cid=?,`name`=?,secret=?,username=? where id=?";
			jdbcTemplate.update(sql, client.getCid(), client.getName(), client.getSecret(), client.getUsername(),
					client.getId());
			return client;
		}
	}

	public void deleteClient(Long id) {
		String sql = "delete from o_client where id=?";
		jdbcTemplate.update(sql, id);
	}

	public Client findOne(Long id) {
		String sql = "select id, `name`, cid, secret, username from o_client where id=?";
		List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Client>(Client.class), id);
		if (clientList.size() == 0) {
			return null;
		}
		return clientList.get(0);
	}

	public List<Client> findAll() {
		String sql = "select id, `name`, cid, secret, username from o_client";
		return jdbcTemplate.query(sql, new BeanPropertyRowMapper<Client>(Client.class));
	}

	public Client findByClientId(String cid) {
		String sql = "select id, `name`, cid, secret, username from o_client where cid=?";
		List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Client>(Client.class), cid);
		if (clientList.size() == 0) {
			return null;
		}
		return clientList.get(0);
	}

	public Client findBySecret(String secret) {
		String sql = "select id, `name`, cid, secret, username from o_client where secret=?";
		List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Client>(Client.class), secret);
		if (clientList.size() == 0) {
			return null;
		}
		return clientList.get(0);
	}

	public Client findByUsernameAndPassword(String username, String password) {
		String sql = "select id, `name`, cid, secret, username from o_client where username=? and `password`=MD5(?)";
		List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Client>(Client.class), username,
				password);
		if (clientList.size() == 0) {
			return null;
		}
		return clientList.get(0);
	}

	private Client findByCode(String code) {
		String sql = "select id, `name`, cid, secret, username from o_client where code=?";
		List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Client>(Client.class), code);
		if (clientList.size() == 0) {
			return null;
		}
		return clientList.get(0);
	}

	public Client findByAccessToken(String accessToken) {
		String sql = "select id, `name`, cid, secret, username from o_client where access_token=? and access_token_time > now()";
		List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Client>(Client.class), accessToken);
		if (clientList.size() == 0) {
			return null;
		}
		return clientList.get(0);
	}

	public Client findByRefreshToken(String refreshToken) {
		String sql = "select id, `name`, cid, secret, username from o_client where refresh_token=? and refresh_token_time > now()";
		List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Client>(Client.class),
				refreshToken);
		if (clientList.size() == 0) {
			return null;
		}
		return clientList.get(0);
	}

	public void updateCode(String cid, String code) {
		jdbcTemplate.update("update o_client set code=?,code_time=? where cid=?", code, System.currentTimeMillis(),
				cid);
	}

	public void updateAccessToken(String cid, String accessToken) {
		jdbcTemplate.update("update o_client set access_token=?,access_token_time=? where cid=?", accessToken,
				System.currentTimeMillis() + C.ACCESS_TOKEN_EXPIRE_IN * 1000, cid);
	}

	public void updateRefreshToken(String cid, String refreshToken) {
		jdbcTemplate.update("update o_client set refresh_token=?,refresh_token_time=? where cid=?", refreshToken,
				System.currentTimeMillis() + C.REFRESH_TOKEN_EXPIRE_IN * 1000, cid);
	}

	public String checkCode(String code) {
		Client client = findByCode(code);
		if (client == null) {
			return null;
		}
		String cid = client.getCid();
		if (StringUtils.isNotBlank(cid)) {
			int count = jdbcTemplate.update("update o_client set code='',code_time=null where code=?", code);
			if (count == 1) {
				return cid;
			}
		}
		return null;
	}

	public boolean checkAccessToken(String accessToken) {
		return jdbcTemplate.queryForList(
				"select cid from o_client where access_token=? and access_token_time/1000 > UNIX_TIMESTAMP()",
				accessToken).size() == 1;
	}

	public boolean checkRefreshToken(String refreshToken) {
		return jdbcTemplate.queryForList(
				"select cid from o_client where refresh_token=? and refresh_token_time/1000 > UNIX_TIMESTAMP()",
				refreshToken).size() == 1;
	}

	public void clearClient(String cid) {
		jdbcTemplate.update(
				"update o_client set code=null,code_time=null,access_token=null,access_token_time=null,refresh_token=null,refresh_token_time=null where cid=?",
				cid);
	}
}
